﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../global/head.h"
#include "../xos/head.h"
#include "socket_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos
{
    ////////////////////////////////////////////////////////////////////////////////////
    // 

    static socket_impl::POOL * pool_ptr = 0;

    ////////////////////////////////////////////////////////////////////////////////////
    //

    socket_impl::socket_impl()
    {
        init_data();
    }

    socket_impl::~socket_impl()
    {
        term();
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::init_data()
    {
        int ret = 0;
        memset( &m_addr, 0, sizeof( sockaddr_in ) );
        m_hSocket = -1;
        m_pUserData = 0;
        return ret;
    }

    int socket_impl::get_addr( POOL *** pppPool, LIST *** pppList )
    {
        int ret = 0;
        if( pppPool )
        {
            *pppPool = &pool_ptr;
        }
        if( pppList )
        {
            *pppList = 0;
        }
        return ret;
    }

    int socket_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int socket_impl::term()
    {
        int nRet = 0;
        init_data();
        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // 
    // udp server init
    // 
    int socket_impl::udp_init( const char * lpszLocalIp, unsigned short usLocalPort )
    {
        int nRet = 0;

        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            m_hSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
            if( -1 == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            int flag = 1, len = sizeof( flag );
            int ret = setsockopt( m_hSocket, SOL_SOCKET, SO_REUSEADDR, &flag, len );
            if( -1 == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            nRet = get_ip_by_name( lpszLocalIp, &dwIp, 0 );
        }

        if( 0 == nRet )
        {
            sockaddr_in & addr = m_addr;
            memset( &addr, 0, sizeof( addr ) );
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usLocalPort );
            nRet = bind( m_hSocket, ( const sockaddr * )&addr, sizeof( sockaddr_in ) );
        }

        return nRet;
    }

    // 
    // udp send
    // 
    int socket_impl::udp_send( const char * lpMsg, int nLen, const char * lpszToIp, unsigned short usToPort )
    {
        int nRet = 0;

        int nToLen = sizeof( sockaddr_in );
        sockaddr_in addr = { 0 };
        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            nRet = get_ip_by_name( lpszToIp, &dwIp, 0 );
            if( 0 != nRet )
            {
                nRet = -1;
            }
        }

        if( 0 == nRet )
        {
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usToPort );
            nRet = sendto( m_hSocket, lpMsg, nLen, MSG_DONTWAIT, ( const struct sockaddr * )&addr, nToLen );
        }

        return nRet;
    }

    // 
    // udp recv
    // 
    int socket_impl::udp_recv( char * lpMsg, int nLen, char * lpszFromIp, unsigned short * lpusFromPort )
    {
        using namespace std;
        int nRet = 0;

        socklen_t tFromLen = sizeof( sockaddr_in );
        sockaddr_in addr = { 0 };

        nRet = ( int )recvfrom( m_hSocket, lpMsg, ( size_t )nLen, MSG_DONTWAIT, ( struct sockaddr * )&addr, &tFromLen );

        if( -1 != nRet )
        {
            strcpy( lpszFromIp, inet_ntoa( addr.sin_addr ) );
            if( lpusFromPort )
            {
                *lpusFromPort = ntohs( addr.sin_port );
            }
        }

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // tcp init
    // 
    int socket_impl::tcp_init( const char * lpszLocalIp, unsigned short usLocalPort, bool bBind )
    {
        int nRet = 0;

        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            m_hSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
            if( -1 == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            int flag = 1, len = sizeof( flag );
            int ret = setsockopt( m_hSocket, SOL_SOCKET, SO_REUSEADDR, &flag, len );
            if( -1 == ret )
            {
                nRet = 1;
            }
        }

        if( ( 0 == nRet ) && bBind )
        {
            nRet = get_ip_by_name( lpszLocalIp, &dwIp, 0 );
        }

        if( ( 0 == nRet ) && bBind )
        {
            sockaddr_in & addr = m_addr;
            memset( &addr, 0, sizeof( addr ) );
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usLocalPort );
            nRet = bind( m_hSocket, ( const sockaddr * )&addr, sizeof( sockaddr_in ) );
        }

        return nRet;
    }

    // 
    // tcp accept
    // 
    int socket_impl::tcp_accept( i_socket ** ppv )
    {
        int nRet = 0;

        int hSocket = -1;
        sockaddr_in addr = { 0 };
        socklen_t nLen = sizeof( sockaddr_in );
        socket_impl * p = 0;
        *ppv = 0;

        if( 0 == nRet )
        {
            hSocket = accept( m_hSocket, ( sockaddr * )&addr, &nLen );
            if( -1 == hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            g_pXos->create( i_xos::XOS_OBJ_SOCKET, ( void ** )&p );

            if( !p )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            memcpy( &p->m_addr, &addr, sizeof( sockaddr_in ) );
            p->m_hSocket = hSocket;
            *ppv = p;
            hSocket = -1;
            p = 0;
        }

        if( -1 != hSocket )
        {
            ::close( hSocket );
            hSocket = -1;
        }

        if( p )
        {
            p->release();
            p = 0;
        }

        return nRet;
    }

    // 
    // tcp connect
    // 
    int socket_impl::tcp_connect( const char * lpszServerIp, unsigned short usServerPort )
    {
        int nRet = 0;

        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            nRet = get_ip_by_name( lpszServerIp, &dwIp, 0 );
        }

        if( 0 == nRet )
        {
            m_hSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

            if( -1 == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            sockaddr_in & addr = m_addr;
            memset( &addr, 0, sizeof( addr ) );

            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usServerPort );

            nRet = connect( m_hSocket, ( const sockaddr * )&addr, sizeof( sockaddr_in ) );
        }

        if( 0 != nRet )
        {
            if( -1 != m_hSocket )
            {
                ::close( m_hSocket );
                m_hSocket = -1;
            }
        }

        return nRet;
    }

    // 
    // tcp侦听
    // 
    int socket_impl::tcp_listen( int nNum )
    {
        int nRet = 0;
        nRet = listen( m_hSocket, nNum );
        return nRet;
    }

    // 
    // tcp send
    // 
    int socket_impl::tcp_send( const char * lpMsg, int nLen )
    {
        int nRet = 0;
        nRet = send( m_hSocket, lpMsg, nLen, 0 );
        return nRet;
    }

    // 
    // tcp recv
    // 
    int socket_impl::tcp_recv( char * lpMsg, int nLen )
    {
        int nRet = 0;
        nRet = recv( m_hSocket, lpMsg, nLen, 0 );
        return nRet;
    }

    int socket_impl::set_async( bool bAsync )
    {
        int nRet = 0;

        int opts = fcntl( m_hSocket, F_GETFL );

        if( opts < 0 )
        {
            nRet = 1;
        }
        else
        {
            if( bAsync )
            {
                opts = opts | O_NONBLOCK;
            }
            else
            {
                opts = opts & ~O_NONBLOCK;
            }
        }

        if( 0 == nRet )
        {
            nRet = fcntl( m_hSocket, F_SETFL, opts );
        }

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::close( int onoff, int linger )
    {
        int nRet = 0;

        if( -1 == m_hSocket )
        {
            return 0;
        }
        {
            struct linger so_linger;
            memset( &so_linger, 0, sizeof( so_linger ) );
            so_linger.l_linger = linger;
            so_linger.l_onoff = onoff;
            setsockopt( m_hSocket,
                SOL_SOCKET,
                SO_LINGER,
                (const char *)&so_linger,
                sizeof( so_linger ) );
        }
        ::close( m_hSocket );
        m_hSocket = -1;

        return nRet;
    }

    int socket_impl::shut_down( int how )
    {
        int ret = 0;

        int sd = SHUT_WR;

        switch( how )
        {
        case XOS_SD_RECEIVE:
            {
                sd = SHUT_RD;
            }
            break;
        case XOS_SD_SEND:
            {
                sd = SHUT_WR;
            }
            break;
        case XOS_SD_BOTH:
            {
                sd = SHUT_RDWR;
            }
            break;
        }

        if( -1 != m_hSocket )
        {
            shutdown( m_hSocket, sd );
        }

        return ret;
    }

    //
    //
    //
    int socket_impl::get_ip_by_name( const char * lpszName, unsigned long * lpdwIp, char * lpszIp )
    {
        int nResult = 1;
        int nRet = 0;

        unsigned long dwIp = 0;
        char ip[100] = { 0 };
        bool bIsName = false;

        if( !lpszName || ( 0 == strlen( lpszName ) ) )
        {
            strcpy( ip, "127.0.0.1" );
            dwIp = htonl( INADDR_ANY );
            nResult = 0;
            nRet = 1;
        }

        if( 0 == nRet )
        { 
            int len = strlen( lpszName );
            for( int i = 0; i < len; i++ )
            {
                bool bState = ( ( lpszName[i] <= '9' ) && ( lpszName[i] >= '0' ) ) || ( lpszName[i] == '.' );
                if( !bState )
                {
                    bIsName = true;
                    break;
                }
            }
        }

        if( 0 == nRet )
        {
            if( bIsName )
            {
                nRet = get_ip_by_name( lpszName, ip );
            }
            else
            {
                strcpy( ip, lpszName );
            }
        }

        if( 0 == nRet )
        {
            dwIp = inet_addr( ip );
        }

        if( 0 == nRet )
        {
            nResult = 0;
        }

        if( 0 == nResult )
        {
            if( lpszIp )
            {
                strcpy( lpszIp, ip );
            }
            if( lpdwIp )
            {
                *lpdwIp = dwIp;
            }
        }

        return nResult;
    }

    int socket_impl::get_ip_by_name( const char * lpszName, char * lpszIP )
    {
        int nRet = 0;

        struct hostent * lpHost = 0;

        if( 0 == nRet )
        {
            lpHost = gethostbyname( lpszName );
            if( !lpHost )
            {
                nRet = 1;
            }
        }

        const char * lpstr = 0;

        if( 0 == nRet )
        {
            lpstr = lpHost->h_addr_list[0];
            if( !lpstr )
            {
                nRet = 2;
            }
        }

        if( 0 == nRet )
        {
            struct in_addr addr = { 0 };
            memcpy( &addr, lpstr, sizeof( addr ) );
            strcpy( lpszIP, inet_ntoa( addr ) );
        }

        return nRet;
    }

    int socket_impl::get_local_addr( char * lpszName, char * lpszIP )
    {
        int nRet = 0;

        char name[1024] = { 0 }, ip[1024] = { 0 };

        if( 0 == nRet )
        {
            nRet = gethostname( name, sizeof( name ) );
        }

        if( 0 == nRet )
        {
            nRet = get_ip_by_name( name, ip );
        }

        if( 0 == nRet )
        {
            if( lpszName )
            {
                strcpy( lpszName, name );
            }
            if( lpszIP )
            {
                strcpy( lpszIP, ip );
            }
        }

        return nRet;
    }

    // 
    // 
    // 
    int socket_impl::get_socket_addr( char * lpszIp, unsigned short * lpusPort )
    {
        using namespace std;
        int nRet = 0;

        sockaddr_in addr = { 0 };
        socklen_t addr_len = sizeof( addr );

        if( 0 == nRet )
        {
            int ret = getsockname( m_hSocket, ( sockaddr * )&addr, &addr_len );

            if( -1 == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            strcpy( lpszIp, inet_ntoa( addr.sin_addr ) );
            *lpusPort = ntohs( addr.sin_port );
        }

        return nRet;
    }

    int socket_impl::get_peer_addr( char * lpszIp, unsigned short * lpusPort )
    {
        int nRet = 0;

        sockaddr_in addr = { 0 };
        socklen_t addr_len = sizeof( addr );

        if( 0 == nRet )
        {
            int ret = getpeername( m_hSocket, ( sockaddr * )&addr, &addr_len );

            if( -1 == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            strcpy( lpszIp, inet_ntoa( addr.sin_addr ) );
            *lpusPort = ntohs( addr.sin_port );
        }

        return nRet;
    }

    // 
    // 
    // 
    int socket_impl::get_ip_port( char * lpszIp, unsigned short * lpusPort )
    {
        int nRet = 0;

        strcpy( lpszIp, inet_ntoa( m_addr.sin_addr ) );
        *lpusPort = ntohs( m_addr.sin_port );

        return nRet;
    }

    void socket_impl::set_addr( void * lpAddr )
    {
        memcpy( &m_addr, lpAddr, sizeof( sockaddr_in ) );
    }

    void socket_impl::set_socket( xos::xos_u64 ulSocket )
    {
        m_hSocket = ( int )ulSocket;
    }

    xos_u64 socket_impl::get_socket()
    {
        return ( xos_u64 )m_hSocket;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get user data
    // 
    void * socket_impl::get_user_data( void ** ppvUserData )
    {
        void * pRet = m_pUserData;

        if( ppvUserData )
        {
            *ppvUserData = m_pUserData;
        }

        return pRet;
    }

    // 
    // set user data
    // 
    int socket_impl::set_user_data( void * pUserData )
    {
        int nRet = 0;
        m_pUserData = pUserData;
        return nRet;
    }

} // xos
